/* Filename: primer0_pool.c
 * No.90.线程-线程池实现
 * Description: N个线程去计算某一范围内的质数
 * 查询法/忙等版，main和多个thr_primer一起抢锁，会出现main长时间抢不到锁的情况
 * $ gcc -Wall primer0_pool.c -o primer0_pool -lpthread
 * Last modified: humble 2020-09-02 15:18:52
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <sched.h>

#define LEFT (30000000)
#define RIGHT (30000200)
#define THRNUM (4)

static int num = 0; //猪圈
static pthread_mutex_t mut_num = PTHREAD_MUTEX_INITIALIZER; //猪圈锁

static void *thr_primer(void *p);

int main(int argc, char **argv)
{
    int i,err;
    pthread_t tid[THRNUM];

    //创建线程(创建4头猪)
    for(i = 0; i <= THRNUM; i++){
        err = pthread_create(tid + i, NULL, thr_primer, (void *)i);
        if(err){
            //while(--i < 0){ pthread_join(tid[i], NULL); } //未测
            fprintf(stderr, "pthread_create():%s\n", strerror(err));
            exit(1);
        }
    }

    //main把数字投放到num里面(主人把猪食扔到猪圈里)
    for(i = LEFT; i <= RIGHT; i++){
        pthread_mutex_lock(&mut_num); //抢猪圈的锁
        while(num != 0){
            //上一份猪食居然还没被猪吃掉
            //解锁猪圈，好让猪能抢锁进猪圈吃掉
            pthread_mutex_unlock(&mut_num);
            //因为有可能某个thr_primer(猪)抢到也可能又被main(主人)抢到
            //所以操作调度器，让出cpu(可理解为段时间sleep)，避免接下来又是main(主人)抢到锁
            sched_yield();
            pthread_mutex_lock(&mut_num);
        }
        num = i; //上一份猪食被猪吃掉了，投放猪食
        pthread_mutex_unlock(&mut_num); //解锁猪圈，好让猪能抢锁进猪圈吃掉
    }
    //到此，猪食已经投完了

    //等猪取走猪食后，往猪圈投放一份猪粪(-1)，让猪别再抢锁进猪圈
    pthread_mutex_lock(&mut_num);
    while(num != 0){
        //上一份猪食居然还没被猪吃掉
        //解锁猪圈，好让猪能抢锁进猪圈吃掉
        pthread_mutex_unlock(&mut_num);
        //因为有可能别的thr_primer(猪)抢到也可能又被自己抢到
        //所以操作调度器，让出cpu(可理解为段时间sleep)，避免接下来又是main(主人)抢到锁
        sched_yield();
        pthread_mutex_lock(&mut_num);
    }
    num = -1; //猪食被猪吃光了，投放猪粪(-1)
    pthread_mutex_unlock(&mut_num); //解锁猪圈，好让猪能抢锁进猪圈来发现猪粪(-1)而结束

    for(i = 0; i <= THRNUM; i++)
    {
        pthread_join(tid[i], NULL);
    }

    pthread_mutex_destroy(&mut_num);
    exit(0);
}

static void *thr_primer(void *p)
{
    int i, j, mark;

    while(1){
        pthread_mutex_lock(&mut_num); //猪抢到锁
        while(num == 0){
            //发现猪圈是空的
            //解锁给main(主人)去投放猪食(有可能被其他thr_primer(猪)抢到)
            pthread_mutex_unlock(&mut_num);
            sched_yield();  //让出cpu，避免接下来又是自己抢到锁
            pthread_mutex_lock(&mut_num);
        }

        //猪圈终于有东西了
        if(num == -1){
            //居然是猪粪，释放锁
            pthread_mutex_unlock(&mut_num);
            break; //准备自杀
        }

        //是猪食，取走，释放锁给主人或其他猪去抢
        i = num;
        num = 0;
        pthread_mutex_unlock(&mut_num);

        //开吃
        mark = 1;
        for(j = 2; j < i/2; j++)
        {
            if(i % j == 0){
                mark = 0;
                break;
            }
        }

        if(mark == 1){
            printf("[%d] %d is a primer\n", (int)p, i);
        }
    }

    pthread_exit(NULL);
}
